﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.NetworkInformation;
using System.Windows.Forms;
using System.Drawing;

namespace PingTest
{
    class NetNode
    {
        public IPAddress ip; // 节点IP地址
        public string dns; // 节点域名
        public string name; // 名称
        public DataGridViewRow row; // 对应的主界面列表里面的一行 

        private LinkedList<long> delays = new LinkedList<long>(); // 测试结果队列        

        private long max; // 最大延迟
        private long min; // 最小延迟
        private long average1; // 平均延迟（200）
        private int lostCount1; // 丢包(200)
        private long average2; // 平均延迟（20）
        private int lostCount2; // 丢包(20)

        private long lastDelay; // 最近一次测试的网络延迟
        private Ping pingClient; // ping测试代理                
        private bool busy; // 忙状态
        private int bufferSize; // ping测试包大小

        public NetNode(string s)
        {            
            if (!IPAddress.TryParse(s, out this.ip))
                this.dns = s;
            //
            max = 0;
            min = Config.PING_TIME_OUT;
            average1 = 0;
            lostCount1 = 0;
            busy = false;
            bufferSize = 32;
            lastDelay = -1;
            delays.Clear();
            //
            pingClient = new Ping();
            pingClient.PingCompleted += new PingCompletedEventHandler(pingClient_PingCompleted);                        
        }

        // 发起一次ping测试
        public void StartPing()
        {
            if (!busy)
            {
                busy = true;
                byte[] buffer = Config.GetBuffer(bufferSize);
                if (ip != null)
                    pingClient.SendAsync(this.ip, Config.PING_TIME_OUT, buffer, null);                
                else
                    pingClient.SendAsync(this.dns, Config.PING_TIME_OUT, buffer, null);                
            }
        }

        // 停止ping测试，清空结果队列
        public void StopPing()
        {
            pingClient.SendAsyncCancel();
            delays.Clear();
            busy = false;
        }

        // 收到ping返回结果
        private void pingClient_PingCompleted(object sender, PingCompletedEventArgs e)
        {            
            if (e.Error == null)
            {
                if (e.Cancelled)
                {
                    //如果点击"取消"相应语句
                    System.Console.Out.WriteLine("用户取消了连通性测试");
                    return;
                }
                else
                {
                    // 如果ping结果正确返回
                    if (e.Reply.Status == IPStatus.Success)
                    {
                        // lryh 添加未丢包记录，将延迟添加到队列
                        this.lastDelay = e.Reply.RoundtripTime;                                                                    
                    }
                    else
                    {
                        // lryh 添加丢包记录
                        this.lastDelay = -1;                        
                    }
                    delays.AddLast(this.lastDelay);
                    if (delays.Count > Config.PING_QUEUE_LONG_SIZE)
                        delays.RemoveFirst();                      
                    //
                    calculate();
                    strategy();                        
                }                
            }
            else
            {
                // 检测出错的情况下           
                System.Console.Out.WriteLine("An error occurred while sending this ping. " + e.Error.InnerException.Message);
            }
            busy = false;
        }

        // 重新计算丢包率、平均延迟、最大最小
        private void calculate()
        {            
            if (this.lastDelay > max)
                max = this.lastDelay;
            else if ((this.lastDelay > 0) && (this.lastDelay < min))
                min = this.lastDelay;  
            //
            long l = 0;
            int c = 0;
            foreach (long t in delays)
            {
                if (t > 0)
                {
                    c++;
                    l += t;
                }
            }
            this.lostCount1 = delays.Count - c;
            if (c > 0)
                this.average1 = l / c;
            else
                this.average1 = -1;
            //            
            l = 0;
            c = 0;
            LinkedListNode<long> lln = delays.Last;
            for (int i = 0; i < Config.PING_QUEUE_SHORT_SIZE; i++)
            {
                if (lln != null)
                {
                    long t = lln.Value;
                    if (t > 0)
                    {
                        c++;
                        l += t;
                    }
                    lln = lln.Previous;
                }
            }
            if (delays.Count > Config.PING_QUEUE_SHORT_SIZE)
                this.lostCount2 = Config.PING_QUEUE_SHORT_SIZE - c;
            else
                this.lostCount2 = delays.Count - c;
            if (c > 0)
                this.average2 = l / c;
            else
                this.average2 = -1;            
        }

        // 判断当前测试条件下网络性能，视情调整测试带宽
        private void strategy()
        {                        
            // 判定条件：已测试100次以上，并且丢包<3%
            if ((delays.Count > Config.PING_QUEUE_LONG_SIZE/2) && (100 * lostCount1 < 3 * delays.Count))
                if (bufferSize < Config.MAX_BUFFER_SIZE)
                {    
                    // ping测试包大小翻倍
                    bufferSize = bufferSize * 2;
                    delays.Clear();
                }
            if ((lostCount2 > 2) || ((delays.Count > 40) && (100 * lostCount1 > 4 * delays.Count)))
                if (bufferSize > Config.MIN_BUFFER_SIZE)
                {
                    // ping测试包大小减半
                    bufferSize = bufferSize / 2;
                    delays.Clear();
                }            
        }

        // 根据丢包率、平均延迟、带宽，综合判定该节点网络性能优劣，更新该节点显示颜色
        public void RefreshRow()
        {            
            int c1 = 0;
            if (delays.Count > 0)
                c1 = (100 * lostCount1) / delays.Count;                        
            int c2 = 0;
            if (delays.Count > Config.PING_QUEUE_SHORT_SIZE)
                c2 = (100 * lostCount2) / Config.PING_QUEUE_SHORT_SIZE; 
            else if (delays.Count > 0)
                c2 = (100 * lostCount2) / delays.Count;
            //
            row.Cells[2].Value = c1.ToString() + "%(" + average1 + "ms)";
            row.Cells[3].Value = c2.ToString() + "%(" + average2 + "ms)";                        
            row.Cells[4].Value = this.bufferSize + "bytes";
            row.Cells[5].Value = this.min + "ms - " + this.max + "ms";
            //
            if (delays.Count < Config.PING_QUEUE_SHORT_SIZE)
                row.DefaultCellStyle.BackColor = Color.White;
            else
            {    
                if ((c2 > Config.THRESHOLD_DISCONNECT_LOST_RATE) 
                    && (this.bufferSize < Config.THRESHOLD_DISCONNECT_BUFFER_SIZE))
                    row.DefaultCellStyle.BackColor = Color.Red;
                else
                {
                    if ((this.bufferSize > Config.THRESHOLD_GOOD_BUFFER_SIZE) 
                        && (this.average1 < Config.THRESHOLD_GOOD_LATENCY)
                        && (c1 < Config.THRESHOLD_GOOD_LOST_RATE))
                        row.DefaultCellStyle.BackColor = Config.COLOR_GOOD;
                    else if ((this.bufferSize < Config.THRESHOLD_BAD_BUFFER_SIZE)
                             || (this.average1 > Config.THRESHOLD_BAD_LATENCY)
                             || (c1 > Config.THRESHOLD_BAD_LOST_RATE))
                        row.DefaultCellStyle.BackColor = Config.COLOR_BAD;
                    else
                        row.DefaultCellStyle.BackColor = Config.COLOR_OTHER;
                }
            }
        }
    }
}
